//{==================================================================================================
//! @file SolveSquare.cpp
//! @date 2013-09-29
//! @mainpage  Программа для решения квадратных уравнений
//! @author Kirill Shcherbatov <kshcherbatov@gmail.com>, 376 group, DCAM MIPT
//! @version 1.0
//}===================================================================================================

//****************************************************************************************************
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <float.h>
//****************************************************************************************************

//****************************************************************************************************
#define _EJC
#define PARANOIC_MODE
//****************************************************************************************************

//****************************************************************************************************
//! @def   OUT
//! @note  -Use OUT to print your text in console. It is checking _EJC declaration.
//! @note  -Only if _EJC is defined then the program prints your text.
//! @note  --You also can define _EJC in compilation command (g++ main.cpp -D _EJC)
//----------------------------------------------------------------------------------------------------
#ifdef _EJC
    #define OUT printf
#else
    #define OUT if (0) printf
#endif // _EJC
//****************************************************************************************************


//****************************************************************************************************
//! @def    VALID
//! @param  CONDITIONAL for checking
//! @param  MESSAGE for displaying if CONDITIONAL is false
//! @note   -It checks the validity of the expression.
//! @note   -Program will use OUT to display MESSAGE
//! @note   -If sended conditional is false, program will be terminated sending 1 message
//----------------------------------------------------------------------------------------------------
#ifdef  PARANOIC_MODE
    #define VALID( CONDITIONAL, MESSAGE )       \
    if (!(CONDITIONAL))                         \
    {                                           \
        OUT  ( MESSAGE );                       \
        exit ( 1 );                             \
    } // VALID in PARANOIC_MODE
#else
    #define VALID( CONDITIONAL, MESSAGE ) \
    (void) CONDITIONAL;
#endif
//****************************************************************************************************

//****************************************************************************************************
//! @def    CHECK
//! @param  CONDITIONAL for checking
//! @param  MESSAGE for displaying if CONDITIONAL is false
//! @note   -It checks the validity of the expression.
//! @note   -Program will use OUT to display MESSAGE
//! @note   -If sended conditional is false, program will be terminated sending 1 message
//! @warning   IN RELIASE MODE IT WILL BE REMOOVED
//----------------------------------------------------------------------------------------------------
#ifdef PARANOIC_MODE
    #define CHECK( CONDITIONAL, MESSAGE )       \
    if (!(CONDITIONAL))                         \
    {                                           \
        OUT  ( MESSAGE );                       \
        exit ( 1 );                             \
    } // CHECK in PARANOIC_MODE
#else
    #define CHECK( CONDITIONAL, MESSAGE )
#endif
//****************************************************************************************************

//****************************************************************************************************
//! @def   IS_ZERO
//! @param  value for comparing
//! @note  -Use IS_ZERO to compere float integers with 0
//----------------------------------------------------------------------------------------------------
#define IS_ZERO( value )  ( fabs (value) < DBL_EPSILON )
//****************************************************************************************************


//****************************************************************************************************
char SolveSquare(double a, double b, double c,
                 double *x1, double *x2);
//****************************************************************************************************

int main (int, const char* argv[])
{
    OUT ("#%s (%s) %s %s:\n", argv[0], __FILE__, __DATE__, __TIME__);
    OUT ("#Решение уравнений вида ax^2 + bx + c = 0 на множестве действительных чисел\n\n");
    OUT ("#Введите коэффициэнты a b c:\n");

    double a=0, b=0, c=0;

    VALID (scanf ("%lg %lg %lg", &a, &b, &c) == 3,  "#Ошибка ввода!\n");

    double x1 = 0, x2 = 0;

    char nRoots = SolveSquare (a, b, c, &x1, &x2);

    switch (nRoots)
    {
    case -1:
        OUT ("#Решением являются все действительные числа.\n");
        break;

    case 0:
        OUT ("#Нет решений на множестве действительных чисел.\n");
        break;

    case 1:
        if (!x1) x1 = 0;
        OUT ("#Уравнение имеет единственное решение на множестве действительных чисел:\n");
        printf ("%lg", x1);
        break;

    case 2:
        OUT ("#Корни уравнения на множестве действительных чисел:\n");
        printf ("%lg %lg\n", x1, x2);
        break;

    default:
        CHECK (0,"Необъявленное возвращаемое значение SolveSquare()!");
        return 1;
    }

    return 0;
}

//****************************************************************************************************
//! @param a коэффициэнт при x^2
//! @param b коэффициэнт при x^1
//! @param c коэффициэнт при x^0
//! @param [out] x1 первый корень уравнения
//! @param [out] x2 второй корень уравнения
//! @return количество корней уравнения
//----------------------------------------------------------------------------------------------------
char SolveSquare (double a, double b, double c,
                  double *x1, double *x2)
{
    CHECK (x1 != NULL, "Передача нулевого адреса в качестве x1!");
    CHECK (x2 != NULL, "Передача нулевого адреса в качестве x1!");
    CHECK (x1 != x2, "Адреса для записи x1 и x2 совпадают!");

    if (! IS_ZERO (a))
    {
        double d = b * b - 4 * a * c;

        if (d > 0)
        {
            *x1 = (-b + sqrt (d)) / (2 * a);
            *x2 = (-b - sqrt (d)) / (2 * a);

            return 2;
        }
        else if (IS_ZERO (d))
        {
            *x1 = -b / (2*a);

            return 1;
        }
        else
            return 0;
    }
    else
    {
        if (! IS_ZERO (b))
        {
            *x1 = -c / b;

            return 1;
        }
        else
            return ! IS_ZERO (c) ? 0 : -1;
    }

    CHECK (0,"Странное поведение SolveSquare(), это строка не должна выполняться!");
}

//------------------------------------------------------------------------
